#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "gameboard.h"
#include "dialog.h"
#include "global.h"
#include "validmove.h"

#define PORT 9864

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
  ui->setupUi(this);
  setupGameBoard();
  board = new Gameboard();
  previousSelected = new Coord(0, 0);
  turn = WHITE;
  player = 0;
  opponentAddress = 0;
  udp = new QUdpSocket();
  udp->bind(QHostAddress::Any, PORT, 0);
  connect(udp, SIGNAL(readyRead()), this, SLOT(receiveData()));
  connectPieces();
  squareSelected = 0;
  prompt = new Dialog(this);
  prompt->show(); // ask user for IP address
}

void MainWindow::selected() {
  QPoint p = this->mapFromGlobal(QCursor::pos());
  int curX = p.rx()/50;
  int curY = 7 - p.ry()/50;
  if (player == WHITE && turn == player) {
	if (board->getColor(curX, curY) == WHITE || squareSelected != 0) {  // can only click on own pieces
	  if (squareSelected == 0) {  // first click
		previousSelected->x = curX;
		previousSelected->y = curY;
		squareSelected = uiBoard[curX][curY];
		squareSelected->setStyleSheet(QString::fromUtf8("background-color: rgb(255, 255, 0);"));
	  }
	  else {  // second click
		if (squareSelected == uiBoard[curX][curY]) {  // deselect
		  if ((curX + curY) % 2)
			squareSelected->setStyleSheet(QString::fromUtf8("background-color: rgb(230, 230, 230);"));
		  else
			squareSelected->setStyleSheet(QString::fromUtf8("background-color: rgb(130, 130, 130);"));
		  squareSelected = 0;
		}
		else {  // move piece
		  if (board->getType(previousSelected->x, previousSelected->y) == ROOK
			  && board->getType(curX, curY) == KING
			  && board->atInitial(previousSelected->x, previousSelected->y)
			  && board->atInitial(curX, curY)) { // castle
			if (curX > previousSelected->x) { // rook on the left
			  for (int i = 1; ;i++) {
				if (previousSelected->x+i == curX) {
				  castle(0, 4, 3, 2);
				  break;
				}
				else if (board->getType(previousSelected->x+i, 0) >= 0) // piece in the way
				  return;
			  }
			}
			else {  // rook on the right
			  for (int i = 1; ;i++) {
				if (previousSelected->x-i == curX) {
				  castle(7, 4, 5, 6);
				  break;
				}
				else if (board->getType(previousSelected->x-i, 0) >= 0) // piece in the way
				  return;
			  }
			}
		  }
		  else if (!validMove(previousSelected->x, previousSelected->y, curX, curY))
			return;
		  else {  // move
			updatePiece(previousSelected->x, previousSelected->y, curX, curY);
			if ((previousSelected->x + previousSelected->y) % 2)
			  uiBoard[previousSelected->x][previousSelected->y]->setStyleSheet(QString::fromUtf8("background-color: rgb(230, 230, 230);"));
			else
			  uiBoard[previousSelected->x][previousSelected->y]->setStyleSheet(QString::fromUtf8("background-color: rgb(130, 130, 130);"));
			sendMove(previousSelected->x, previousSelected->y, curX, curY);
		  }
		  turn = BLACK;
		  squareSelected = 0;
		  //player = BLACK; // for testing purposes
		  //displayBoard(); // for testing purposes
		}
	  }
	}
  }
  else if (player == BLACK && turn == player) {
	if (board->getColor(7-curX, 7-curY) == BLACK || squareSelected != 0) {
	  if (squareSelected == 0) {  // first click
		previousSelected->x = curX;
		previousSelected->y = curY;
		squareSelected = uiBoard[curX][curY];
		squareSelected->setStyleSheet(QString::fromUtf8("background-color: rgb(255, 255, 0);"));
	  }
	  else {  // second click
		if (squareSelected == uiBoard[curX][curY]) {  // deselect
		  if ((curX + curY) % 2)
			uiBoard[previousSelected->x][previousSelected->y]->setStyleSheet(QString::fromUtf8("background-color: rgb(230, 230, 230);"));
		  else
			uiBoard[previousSelected->x][previousSelected->y]->setStyleSheet(QString::fromUtf8("background-color: rgb(130, 130, 130);"));
		  squareSelected = 0;
		}
		else {  // move piece
		  if (board->getType(7-previousSelected->x, 7-previousSelected->y) == ROOK
			  && board->getType(7-curX, 7-curY) == KING
			  && board->atInitial(7-previousSelected->x, 7-previousSelected->y)
			  && board->atInitial(7-curX, 7-curY)) {  // castle
			if (curX > previousSelected->x) { // rook on the left
			  for (int i = 1; ;i++) {
				if (previousSelected->x+i == curX) {
				  castle(7, 4, 5, 6);
				  break;
				}
				else if (board->getType(7-previousSelected->x-i, 7) >= 0) // piece in the way
				  return;
			  }
			}
			else {  // rook on the right
			  for (int i = 1; ;i++) {
				if (previousSelected->x-i == curX) {
				  castle(0, 4, 3, 2);
				  break;
				}
				else if (board->getType(7-previousSelected->x+i, 7) >= 0) // piece in the way
				  return;
			  }
			}
		  }
		  else if (!validMove(7 - previousSelected->x, 7 - previousSelected->y, 7 - curX, 7 - curY))
			return;
		  else {
			updatePiece(7 - previousSelected->x, 7 - previousSelected->y, 7-curX, 7-curY);
			if ((previousSelected->x + previousSelected->y) % 2)
			  uiBoard[previousSelected->x][previousSelected->y]->setStyleSheet(QString::fromUtf8("background-color: rgb(230, 230, 230);"));
			else
			  uiBoard[previousSelected->x][previousSelected->y]->setStyleSheet(QString::fromUtf8("background-color: rgb(130, 130, 130);"));
			sendMove(7-previousSelected->x, 7-previousSelected->y, 7-curX, 7-curY);
		  }
		  turn = WHITE;
		  squareSelected = 0;
		  //player = WHITE; // for testing purposes
		  //displayBoard(); // for testing purposes
		}
	  }
	}
  }
}

MainWindow::~MainWindow() {
  delete ui;
  delete board;
  delete udp;
}

// Display the whole board with the correct perspective
// depending on whether the player is WHITE or BLACK
void MainWindow::displayBoard() {
  char *tempFileName;
  if (player == WHITE) {
	for (int x = 0; x < 8; x++) {
	  for (int y = 0; y < 8; y++) {
		if ((board != 0) && (tempFileName = board->getFileName(x, y)) != 0) {
		  uiBoard[x][y]->setIcon(QIcon(QPixmap(QString(tempFileName))));
		  uiBoard[x][y]->setIconSize(QSize(QPixmap(QString(tempFileName)).size()));
		}
		else {
		  uiBoard[x][y]->setIcon(QIcon());
		}
	  }
	}
  }
  else if (player == BLACK) {
	for (int x = 0; x < 8; x++ ) {
	  for (int y = 0; y < 8; y++) {
		if ((board != 0) && (tempFileName = board->getFileName(x, y)) != 0) {
		  uiBoard[7-x][7-y]->setIcon(QIcon(QPixmap(QString(tempFileName))));
		  uiBoard[7-x][7-y]->setIconSize(QSize(QPixmap(QString(tempFileName)).size()));
		}
		else {
		  uiBoard[7-x][7-y]->setIcon(QIcon());
		}
	  }
	}
  }
}

// Updates piece on board and displays it
// with respect to that player's perspective
void MainWindow::updatePiece(int oldx, int oldy, int newx, int newy) {
  board->updatePosition(oldx, oldy, newx, newy);
  if (player == WHITE) {
	uiBoard[oldx][oldy]->setIcon(QIcon());
	uiBoard[newx][newy]->setIcon(QIcon(QPixmap(QString(board->getFileName(newx, newy)))));
	uiBoard[newx][newy]->setIconSize(QPixmap(QString(board->getFileName(newx, newy))).size());
  }
  else {
	uiBoard[7-oldx][7-oldy]->setIcon(QIcon());
	uiBoard[7-newx][7-newy]->setIcon(QIcon(QPixmap(QString(board->getFileName(newx, newy)))));
	uiBoard[7-newx][7-newy]->setIconSize(QPixmap(QString(board->getFileName(newx, newy))).size());
  }
}

void MainWindow::castle(int rookX, int kingX, int newRookX, int newKingX) {
  if (player == WHITE) {
	board->updatePosition(rookX, 0, newRookX, 0);
	board->updatePosition(kingX, 0, newKingX, 0);
	uiBoard[rookX][0]->setIcon(QIcon());
	uiBoard[newRookX][0]->setIcon(QIcon(QPixmap(QString(board->getFileName(newRookX, 0)))));
	uiBoard[newRookX][0]->setIconSize(QPixmap(QString(board->getFileName(newRookX, 0))).size());
	uiBoard[kingX][0]->setIcon(QIcon());
	uiBoard[newKingX][0]->setIcon(QIcon(QPixmap(QString(board->getFileName(newKingX, 0)))));
	uiBoard[newKingX][0]->setIconSize(QPixmap(QString(board->getFileName(newKingX, 0))).size());
	if ((rookX) % 2)
	  uiBoard[rookX][0]->setStyleSheet(QString::fromUtf8("background-color: rgb(230, 230, 230);"));
	else
	  uiBoard[rookX][0]->setStyleSheet(QString::fromUtf8("background-color: rgb(130, 130, 130);"));
  }
  else {
	board->updatePosition(rookX, 7, newRookX, 7);
	board->updatePosition(kingX, 7, newKingX, 7);
	uiBoard[7-rookX][0]->setIcon(QIcon());
	uiBoard[7-newRookX][0]->setIcon(QIcon(QPixmap(QString(board->getFileName(newRookX, 7)))));
	uiBoard[7-newRookX][0]->setIconSize(QPixmap(QString(board->getFileName(newRookX, 7))).size());
	uiBoard[7-kingX][0]->setIcon(QIcon());
	uiBoard[7-newKingX][0]->setIcon(QIcon(QPixmap(QString(board->getFileName(newKingX, 7)))));
	uiBoard[7-newKingX][0]->setIconSize(QPixmap(QString(board->getFileName(newKingX, 7))).size());
	if ((7-rookX) % 2)
	  uiBoard[7-rookX][0]->setStyleSheet(QString::fromUtf8("background-color: rgb(230, 230, 230);"));
	else
	  uiBoard[7-rookX][0]->setStyleSheet(QString::fromUtf8("background-color: rgb(130, 130, 130);"));
  }
  sendCastle(rookX, kingX, newRookX, newKingX);
}

void MainWindow::sendMove(int oldx, int oldy, int newx, int newy) {
  char *data = new char[5];
  data[0] = static_cast<char>(VALID_MOVE);
  data[1] = static_cast<char>(oldx);
  data[2] = static_cast<char>(oldy);
  data[3] = static_cast<char>(newx);
  data[4] = static_cast<char>(newy);
  udp->writeDatagram(data, 5, *opponentAddress, PORT);
  delete[] data;
}

void MainWindow::sendCastle(int rookX, int kingX, int newRookX, int newKingX) {
  char *data = new char[5];
  data[0] = static_cast<char>(CASTLE);
  data[1] = static_cast<char>(rookX);
  data[2] = static_cast<char>(kingX);
  data[3] = static_cast<char>(newRookX);
  data[4] = static_cast<char>(newKingX);
  udp->writeDatagram(data, 5, *opponentAddress, PORT);
  delete[] data;
}

void MainWindow::decideColors() {
  if (player == 0) {
	char data = static_cast<char>(COLOR_DECIDED);
	udp->writeDatagram(&data, 1, *opponentAddress, PORT);
  }
}

void MainWindow::sendMessage() {
  int messageSize = ui->chatInput->text().size();
  if (messageSize > 0) {
	QByteArray *chatInput = new QByteArray(1, MESSAGE);
	chatInput->append(ui->chatInput->text());
	udp->writeDatagram(*chatInput, messageSize+1, *opponentAddress, PORT);
	ui->chatWindow->document()->setPlainText(ui->chatWindow->document()->toPlainText().append("You: "));
	ui->chatWindow->document()->setPlainText(ui->chatWindow->document()->toPlainText().append(ui->chatInput->text().toUtf8() += '\n'));
	delete chatInput;
	ui->chatInput->clear();
  }
}

void MainWindow::receiveData() {
  int messageSize = udp->pendingDatagramSize();
  char *buffer = new char[messageSize+2]; // 2 extra for \n and \0
  udp->readDatagram(buffer, messageSize);
  if (buffer[0] == VALID_MOVE || buffer[0] == CASTLE) {
	if (buffer[0] == VALID_MOVE) {
	  updatePiece(buffer[1], buffer[2], buffer[3], buffer[4]);
	}
	else if (buffer[0] == CASTLE) {
	  if (player == WHITE) {
		board->updatePosition(buffer[1], 7, buffer[3], 7);
		board->updatePosition(buffer[2], 7, buffer[4], 7);
	  }
	  else {
		board->updatePosition(buffer[1], 0, buffer[3], 0);
		board->updatePosition(buffer[2], 0, buffer[4], 0);
	  }
	  displayBoard();
	}
	if (turn == BLACK)
	  turn = WHITE;
	else
	  turn = BLACK;
  }
  else if (buffer[0] == COLOR_DECIDED) {
	player = BLACK;
	if (prompt == 0)
	  displayBoard();
  }
  else if (buffer[0] == COLOR_DECIDED_ACK) {
	player = WHITE;
	displayBoard();
  }
  else if (buffer[0] == MESSAGE) {
	buffer[messageSize] = '\n';
	buffer[messageSize+1] = '\0';
	ui->chatWindow->document()->setPlainText(ui->chatWindow->document()->toPlainText().append("Opponent: "));
	ui->chatWindow->document()->setPlainText(ui->chatWindow->document()->toPlainText().append(buffer+1));
	QTextCursor c = ui->chatWindow->textCursor();
	c.movePosition(QTextCursor::End);
	ui->chatWindow->setTextCursor(c);
  }
  delete[] buffer;
}

void MainWindow::removePiece(int x, int y) {
  board->removePiece(x, y);
  if (player == WHITE)
	uiBoard[x][y]->setIcon(QIcon());
  else
	uiBoard[7-x][7-y]->setIcon(QIcon());
}

void MainWindow::setupGameBoard() {
  uiBoard[0][0] = ui->a1;
  uiBoard[1][0] = ui->b1;
  uiBoard[2][0] = ui->c1;
  uiBoard[3][0] = ui->d1;
  uiBoard[4][0] = ui->e1;
  uiBoard[5][0] = ui->f1;
  uiBoard[6][0] = ui->g1;
  uiBoard[7][0] = ui->h1;
  uiBoard[0][1] = ui->a2;
  uiBoard[1][1] = ui->b2;
  uiBoard[2][1] = ui->c2;
  uiBoard[3][1] = ui->d2;
  uiBoard[4][1] = ui->e2;
  uiBoard[5][1] = ui->f2;
  uiBoard[6][1] = ui->g2;
  uiBoard[7][1] = ui->h2;
  uiBoard[0][2] = ui->a3;
  uiBoard[1][2] = ui->b3;
  uiBoard[2][2] = ui->c3;
  uiBoard[3][2] = ui->d3;
  uiBoard[4][2] = ui->e3;
  uiBoard[5][2] = ui->f3;
  uiBoard[6][2] = ui->g3;
  uiBoard[7][2] = ui->h3;
  uiBoard[0][3] = ui->a4;
  uiBoard[1][3] = ui->b4;
  uiBoard[2][3] = ui->c4;
  uiBoard[3][3] = ui->d4;
  uiBoard[4][3] = ui->e4;
  uiBoard[5][3] = ui->f4;
  uiBoard[6][3] = ui->g4;
  uiBoard[7][3] = ui->h4;
  uiBoard[0][4] = ui->a5;
  uiBoard[1][4] = ui->b5;
  uiBoard[2][4] = ui->c5;
  uiBoard[3][4] = ui->d5;
  uiBoard[4][4] = ui->e5;
  uiBoard[5][4] = ui->f5;
  uiBoard[6][4] = ui->g5;
  uiBoard[7][4] = ui->h5;
  uiBoard[0][5] = ui->a6;
  uiBoard[1][5] = ui->b6;
  uiBoard[2][5] = ui->c6;
  uiBoard[3][5] = ui->d6;
  uiBoard[4][5] = ui->e6;
  uiBoard[5][5] = ui->f6;
  uiBoard[6][5] = ui->g6;
  uiBoard[7][5] = ui->h6;
  uiBoard[0][6] = ui->a7;
  uiBoard[1][6] = ui->b7;
  uiBoard[2][6] = ui->c7;
  uiBoard[3][6] = ui->d7;
  uiBoard[4][6] = ui->e7;
  uiBoard[5][6] = ui->f7;
  uiBoard[6][6] = ui->g7;
  uiBoard[7][6] = ui->h7;
  uiBoard[0][7] = ui->a8;
  uiBoard[1][7] = ui->b8;
  uiBoard[2][7] = ui->c8;
  uiBoard[3][7] = ui->d8;
  uiBoard[4][7] = ui->e8;
  uiBoard[5][7] = ui->f8;
  uiBoard[6][7] = ui->g8;
  uiBoard[7][7] = ui->h8;
}

void MainWindow::connectPieces() {
  connect(uiBoard[0][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[0][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[1][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[2][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[3][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[4][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[5][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[6][7], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][0], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][1], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][2], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][3], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][4], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][5], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][6], SIGNAL(clicked()), this, SLOT(selected()));
  connect(uiBoard[7][7], SIGNAL(clicked()), this, SLOT(selected()));
}

void MainWindow::mainGame(QString address) {
  if (opponentAddress == 0)
	opponentAddress = new QHostAddress(address);
  prompt->close();
  delete prompt;    // deallocate IP dialog memory
  prompt = 0;
  char ack = static_cast<char>(COLOR_DECIDED_ACK);
  udp->writeDatagram(&ack, 1, *opponentAddress, PORT);
  show();           // show main window
  decideColors();
  displayBoard();
  ui->chatInput->connect(ui->chatInput, SIGNAL(returnPressed()), this, SLOT(sendMessage()));
}
